{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**问题：**\n",
    "假设你正在为一个电影推荐系统设计一个简单的KNN算法。我们有以下一些用户的电影评分数据，数据由两个特征组成：用户对电影A和电影B的评分，分别在1-5之间。用户的标签（电影类型偏好）是动作片（标签0）或者是喜剧片（标签1）。我们有一个新用户，他给电影A评分为3，电影B评分为4。请问这个用户可能偏好哪种类型的电影？\n",
    "\n",
    "**数据：**\n",
    "\n",
    "| 用户   | 电影A评分 | 电影B评分 | 偏好类型 |\n",
    "| ------ | --------- | --------- | -------- |\n",
    "| 用户1  | 5         | 1         | 动作片   |\n",
    "| 用户2  | 4         | 2         | 动作片   |\n",
    "| 用户3  | 2         | 5         | 喜剧片   |\n",
    "| 用户4  | 1         | 4         | 喜剧片   |\n",
    "| 用户5  | 3         | 2         | 动作片   |\n",
    "| 用户6  | 2         | 5         | 喜剧片   |\n",
    "\n",
    "你需要做以下步骤：\n",
    "1. 构造数据\n",
    "2. 创建KNN模型\n",
    "3. 使用数据训练模型\n",
    "4. 预测新用户的喜好"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 0. 引入核心包"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from sklearn.neighbors import KNeighborsClassifier"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1. X, y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "X = np.array([[5, 1], [4, 2], [2, 5], [1, 4], [3, 2], [2, 5]])\n",
    "\n",
    "y = np.array([0, 0, 1, 1, 0, 1])  # 0表示动作片，1表示喜剧片"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2. 创建 KNN 模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define k for KNN\n",
    "k = 1\n",
    "knn = KNeighborsClassifier(n_neighbors=k)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3. 训练模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>#sk-container-id-2 {\n",
       "  /* Definition of color scheme common for light and dark mode */\n",
       "  --sklearn-color-text: #000;\n",
       "  --sklearn-color-text-muted: #666;\n",
       "  --sklearn-color-line: gray;\n",
       "  /* Definition of color scheme for unfitted estimators */\n",
       "  --sklearn-color-unfitted-level-0: #fff5e6;\n",
       "  --sklearn-color-unfitted-level-1: #f6e4d2;\n",
       "  --sklearn-color-unfitted-level-2: #ffe0b3;\n",
       "  --sklearn-color-unfitted-level-3: chocolate;\n",
       "  /* Definition of color scheme for fitted estimators */\n",
       "  --sklearn-color-fitted-level-0: #f0f8ff;\n",
       "  --sklearn-color-fitted-level-1: #d4ebff;\n",
       "  --sklearn-color-fitted-level-2: #b3dbfd;\n",
       "  --sklearn-color-fitted-level-3: cornflowerblue;\n",
       "\n",
       "  /* Specific color for light theme */\n",
       "  --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n",
       "  --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-icon: #696969;\n",
       "\n",
       "  @media (prefers-color-scheme: dark) {\n",
       "    /* Redefinition of color scheme for dark theme */\n",
       "    --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n",
       "    --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-icon: #878787;\n",
       "  }\n",
       "}\n",
       "\n",
       "#sk-container-id-2 {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 pre {\n",
       "  padding: 0;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 input.sk-hidden--visually {\n",
       "  border: 0;\n",
       "  clip: rect(1px 1px 1px 1px);\n",
       "  clip: rect(1px, 1px, 1px, 1px);\n",
       "  height: 1px;\n",
       "  margin: -1px;\n",
       "  overflow: hidden;\n",
       "  padding: 0;\n",
       "  position: absolute;\n",
       "  width: 1px;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-dashed-wrapped {\n",
       "  border: 1px dashed var(--sklearn-color-line);\n",
       "  margin: 0 0.4em 0.5em 0.4em;\n",
       "  box-sizing: border-box;\n",
       "  padding-bottom: 0.4em;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-container {\n",
       "  /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n",
       "     but bootstrap.min.css set `[hidden] { display: none !important; }`\n",
       "     so we also need the `!important` here to be able to override the\n",
       "     default hidden behavior on the sphinx rendered scikit-learn.org.\n",
       "     See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n",
       "  display: inline-block !important;\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-text-repr-fallback {\n",
       "  display: none;\n",
       "}\n",
       "\n",
       "div.sk-parallel-item,\n",
       "div.sk-serial,\n",
       "div.sk-item {\n",
       "  /* draw centered vertical line to link estimators */\n",
       "  background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n",
       "  background-size: 2px 100%;\n",
       "  background-repeat: no-repeat;\n",
       "  background-position: center center;\n",
       "}\n",
       "\n",
       "/* Parallel-specific style estimator block */\n",
       "\n",
       "#sk-container-id-2 div.sk-parallel-item::after {\n",
       "  content: \"\";\n",
       "  width: 100%;\n",
       "  border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n",
       "  flex-grow: 1;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-parallel {\n",
       "  display: flex;\n",
       "  align-items: stretch;\n",
       "  justify-content: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-parallel-item {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-parallel-item:first-child::after {\n",
       "  align-self: flex-end;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-parallel-item:last-child::after {\n",
       "  align-self: flex-start;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-parallel-item:only-child::after {\n",
       "  width: 0;\n",
       "}\n",
       "\n",
       "/* Serial-specific style estimator block */\n",
       "\n",
       "#sk-container-id-2 div.sk-serial {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "  align-items: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  padding-right: 1em;\n",
       "  padding-left: 1em;\n",
       "}\n",
       "\n",
       "\n",
       "/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n",
       "clickable and can be expanded/collapsed.\n",
       "- Pipeline and ColumnTransformer use this feature and define the default style\n",
       "- Estimators will overwrite some part of the style using the `sk-estimator` class\n",
       "*/\n",
       "\n",
       "/* Pipeline and ColumnTransformer style (default) */\n",
       "\n",
       "#sk-container-id-2 div.sk-toggleable {\n",
       "  /* Default theme specific background. It is overwritten whether we have a\n",
       "  specific estimator or a Pipeline/ColumnTransformer */\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "/* Toggleable label */\n",
       "#sk-container-id-2 label.sk-toggleable__label {\n",
       "  cursor: pointer;\n",
       "  display: flex;\n",
       "  width: 100%;\n",
       "  margin-bottom: 0;\n",
       "  padding: 0.5em;\n",
       "  box-sizing: border-box;\n",
       "  text-align: center;\n",
       "  align-items: start;\n",
       "  justify-content: space-between;\n",
       "  gap: 0.5em;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 label.sk-toggleable__label .caption {\n",
       "  font-size: 0.6rem;\n",
       "  font-weight: lighter;\n",
       "  color: var(--sklearn-color-text-muted);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 label.sk-toggleable__label-arrow:before {\n",
       "  /* Arrow on the left of the label */\n",
       "  content: \"▸\";\n",
       "  float: left;\n",
       "  margin-right: 0.25em;\n",
       "  color: var(--sklearn-color-icon);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 label.sk-toggleable__label-arrow:hover:before {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "/* Toggleable content - dropdown */\n",
       "\n",
       "#sk-container-id-2 div.sk-toggleable__content {\n",
       "  display: none;\n",
       "  text-align: left;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-toggleable__content.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-toggleable__content pre {\n",
       "  margin: 0.2em;\n",
       "  border-radius: 0.25em;\n",
       "  color: var(--sklearn-color-text);\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-toggleable__content.fitted pre {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n",
       "  /* Expand drop-down */\n",
       "  display: block;\n",
       "  width: 100%;\n",
       "  overflow: visible;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n",
       "  content: \"▾\";\n",
       "}\n",
       "\n",
       "/* Pipeline/ColumnTransformer-specific style */\n",
       "\n",
       "#sk-container-id-2 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator-specific style */\n",
       "\n",
       "/* Colorize estimator box */\n",
       "#sk-container-id-2 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-label label.sk-toggleable__label,\n",
       "#sk-container-id-2 div.sk-label label {\n",
       "  /* The background is the default theme color */\n",
       "  color: var(--sklearn-color-text-on-default-background);\n",
       "}\n",
       "\n",
       "/* On hover, darken the color of the background */\n",
       "#sk-container-id-2 div.sk-label:hover label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "/* Label box, darken color on hover, fitted */\n",
       "#sk-container-id-2 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator label */\n",
       "\n",
       "#sk-container-id-2 div.sk-label label {\n",
       "  font-family: monospace;\n",
       "  font-weight: bold;\n",
       "  display: inline-block;\n",
       "  line-height: 1.2em;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-label-container {\n",
       "  text-align: center;\n",
       "}\n",
       "\n",
       "/* Estimator-specific */\n",
       "#sk-container-id-2 div.sk-estimator {\n",
       "  font-family: monospace;\n",
       "  border: 1px dotted var(--sklearn-color-border-box);\n",
       "  border-radius: 0.25em;\n",
       "  box-sizing: border-box;\n",
       "  margin-bottom: 0.5em;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-estimator.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "/* on hover */\n",
       "#sk-container-id-2 div.sk-estimator:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-estimator.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Specification for estimator info (e.g. \"i\" and \"?\") */\n",
       "\n",
       "/* Common style for \"i\" and \"?\" */\n",
       "\n",
       ".sk-estimator-doc-link,\n",
       "a:link.sk-estimator-doc-link,\n",
       "a:visited.sk-estimator-doc-link {\n",
       "  float: right;\n",
       "  font-size: smaller;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1em;\n",
       "  height: 1em;\n",
       "  width: 1em;\n",
       "  text-decoration: none !important;\n",
       "  margin-left: 0.5em;\n",
       "  text-align: center;\n",
       "  /* unfitted */\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted,\n",
       "a:link.sk-estimator-doc-link.fitted,\n",
       "a:visited.sk-estimator-doc-link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "div.sk-estimator:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "/* Span, style for the box shown on hovering the info icon */\n",
       ".sk-estimator-doc-link span {\n",
       "  display: none;\n",
       "  z-index: 9999;\n",
       "  position: relative;\n",
       "  font-weight: normal;\n",
       "  right: .2ex;\n",
       "  padding: .5ex;\n",
       "  margin: .5ex;\n",
       "  width: min-content;\n",
       "  min-width: 20ex;\n",
       "  max-width: 50ex;\n",
       "  color: var(--sklearn-color-text);\n",
       "  box-shadow: 2pt 2pt 4pt #999;\n",
       "  /* unfitted */\n",
       "  background: var(--sklearn-color-unfitted-level-0);\n",
       "  border: .5pt solid var(--sklearn-color-unfitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted span {\n",
       "  /* fitted */\n",
       "  background: var(--sklearn-color-fitted-level-0);\n",
       "  border: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link:hover span {\n",
       "  display: block;\n",
       "}\n",
       "\n",
       "/* \"?\"-specific style due to the `<a>` HTML tag */\n",
       "\n",
       "#sk-container-id-2 a.estimator_doc_link {\n",
       "  float: right;\n",
       "  font-size: 1rem;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1rem;\n",
       "  height: 1rem;\n",
       "  width: 1rem;\n",
       "  text-decoration: none;\n",
       "  /* unfitted */\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 a.estimator_doc_link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "#sk-container-id-2 a.estimator_doc_link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 a.estimator_doc_link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "\n",
       ".estimator-table summary {\n",
       "    padding: .5rem;\n",
       "    font-family: monospace;\n",
       "    cursor: pointer;\n",
       "}\n",
       "\n",
       ".estimator-table details[open] {\n",
       "    padding-left: 0.1rem;\n",
       "    padding-right: 0.1rem;\n",
       "    padding-bottom: 0.3rem;\n",
       "}\n",
       "\n",
       ".estimator-table .parameters-table {\n",
       "    margin-left: auto !important;\n",
       "    margin-right: auto !important;\n",
       "}\n",
       "\n",
       ".estimator-table .parameters-table tr:nth-child(odd) {\n",
       "    background-color: #fff;\n",
       "}\n",
       "\n",
       ".estimator-table .parameters-table tr:nth-child(even) {\n",
       "    background-color: #f6f6f6;\n",
       "}\n",
       "\n",
       ".estimator-table .parameters-table tr:hover {\n",
       "    background-color: #e0e0e0;\n",
       "}\n",
       "\n",
       ".estimator-table table td {\n",
       "    border: 1px solid rgba(106, 105, 104, 0.232);\n",
       "}\n",
       "\n",
       ".user-set td {\n",
       "    color:rgb(255, 94, 0);\n",
       "    text-align: left;\n",
       "}\n",
       "\n",
       ".user-set td.value pre {\n",
       "    color:rgb(255, 94, 0) !important;\n",
       "    background-color: transparent !important;\n",
       "}\n",
       "\n",
       ".default td {\n",
       "    color: black;\n",
       "    text-align: left;\n",
       "}\n",
       "\n",
       ".user-set td i,\n",
       ".default td i {\n",
       "    color: black;\n",
       "}\n",
       "\n",
       ".copy-paste-icon {\n",
       "    background-image: url();\n",
       "    background-repeat: no-repeat;\n",
       "    background-size: 14px 14px;\n",
       "    background-position: 0;\n",
       "    display: inline-block;\n",
       "    width: 14px;\n",
       "    height: 14px;\n",
       "    cursor: pointer;\n",
       "}\n",
       "</style><body><div id=\"sk-container-id-2\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>KNeighborsClassifier(n_neighbors=1)</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-2\" type=\"checkbox\" checked><label for=\"sk-estimator-id-2\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow\"><div><div>KNeighborsClassifier</div></div><div><a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.7/modules/generated/sklearn.neighbors.KNeighborsClassifier.html\">?<span>Documentation for KNeighborsClassifier</span></a><span class=\"sk-estimator-doc-link fitted\">i<span>Fitted</span></span></div></label><div class=\"sk-toggleable__content fitted\" data-param-prefix=\"\">\n",
       "        <div class=\"estimator-table\">\n",
       "            <details>\n",
       "                <summary>Parameters</summary>\n",
       "                <table class=\"parameters-table\">\n",
       "                  <tbody>\n",
       "                    \n",
       "        <tr class=\"user-set\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('n_neighbors',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">n_neighbors&nbsp;</td>\n",
       "            <td class=\"value\">1</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('weights',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">weights&nbsp;</td>\n",
       "            <td class=\"value\">&#x27;uniform&#x27;</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('algorithm',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">algorithm&nbsp;</td>\n",
       "            <td class=\"value\">&#x27;auto&#x27;</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('leaf_size',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">leaf_size&nbsp;</td>\n",
       "            <td class=\"value\">30</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('p',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">p&nbsp;</td>\n",
       "            <td class=\"value\">2</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('metric',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">metric&nbsp;</td>\n",
       "            <td class=\"value\">&#x27;minkowski&#x27;</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('metric_params',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">metric_params&nbsp;</td>\n",
       "            <td class=\"value\">None</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('n_jobs',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">n_jobs&nbsp;</td>\n",
       "            <td class=\"value\">None</td>\n",
       "        </tr>\n",
       "    \n",
       "                  </tbody>\n",
       "                </table>\n",
       "            </details>\n",
       "        </div>\n",
       "    </div></div></div></div></div><script>function copyToClipboard(text, element) {\n",
       "    // Get the parameter prefix from the closest toggleable content\n",
       "    const toggleableContent = element.closest('.sk-toggleable__content');\n",
       "    const paramPrefix = toggleableContent ? toggleableContent.dataset.paramPrefix : '';\n",
       "    const fullParamName = paramPrefix ? `${paramPrefix}${text}` : text;\n",
       "\n",
       "    const originalStyle = element.style;\n",
       "    const computedStyle = window.getComputedStyle(element);\n",
       "    const originalWidth = computedStyle.width;\n",
       "    const originalHTML = element.innerHTML.replace('Copied!', '');\n",
       "\n",
       "    navigator.clipboard.writeText(fullParamName)\n",
       "        .then(() => {\n",
       "            element.style.width = originalWidth;\n",
       "            element.style.color = 'green';\n",
       "            element.innerHTML = \"Copied!\";\n",
       "\n",
       "            setTimeout(() => {\n",
       "                element.innerHTML = originalHTML;\n",
       "                element.style = originalStyle;\n",
       "            }, 2000);\n",
       "        })\n",
       "        .catch(err => {\n",
       "            console.error('Failed to copy:', err);\n",
       "            element.style.color = 'red';\n",
       "            element.innerHTML = \"Failed!\";\n",
       "            setTimeout(() => {\n",
       "                element.innerHTML = originalHTML;\n",
       "                element.style = originalStyle;\n",
       "            }, 2000);\n",
       "        });\n",
       "    return false;\n",
       "}\n",
       "\n",
       "document.querySelectorAll('.fa-regular.fa-copy').forEach(function(element) {\n",
       "    const toggleableContent = element.closest('.sk-toggleable__content');\n",
       "    const paramPrefix = toggleableContent ? toggleableContent.dataset.paramPrefix : '';\n",
       "    const paramName = element.parentElement.nextElementSibling.textContent.trim();\n",
       "    const fullParamName = paramPrefix ? `${paramPrefix}${paramName}` : paramName;\n",
       "\n",
       "    element.setAttribute('title', fullParamName);\n",
       "});\n",
       "</script></body>"
      ],
      "text/plain": [
       "KNeighborsClassifier(n_neighbors=1)"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "knn.fit(X, y)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 4. 用模型推理(预测)用户的喜好"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "new_user = np.array([[3, 4]])\n",
    "prediction = knn.predict(new_user)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 5. 数据可视化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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Z4ewsDAoFArs3LkTAwcO5BySURj1HCMiIiKiuoSFEREREZESCyMiIiIiJRZGREREREosjIiIiIiUWBgRERERKbEwIiIiIlJiYURERESkxMKIiIiISImFEREREZESCyMiIiIiJRZGREREREosjIiIiIiUWBgRERERKbEwIiIiIlJiYURERESkxMKIiIiISImFEREREZESCyMiIiIiJRZGREREREosjIiIiIiUWBgRERERKbEwIiIiIlJiYURERESkxMKIiIiISImFEREREZESCyMiIiIiJRZGREREREosjIiIiIiUjFoY7du3D4MGDYKHhwckEgm2bdtWYf/ExERIJBKN19mzZ2snYCIiIjJrUmNu/N69e+jYsSMmTJiA4cOHV3m9c+fOwdHRUfW+UaNGhgiPiIiIHjFGLYz69euHfv366byeq6srGjRooP+AiIiI6JFm1MKougICAlBQUIC2bdti/vz56N27d7l95XI55HK56n1OTg4AQKFQQKFQ6DWusvH0PW5dYe75AeafI/MzfeaeI/MzfYbKsbY+M4kQQtTKliohkUiwdetWDBkypNw+586dw759+xAYGAi5XI5vvvkG0dHRSExMRI8ePbSus3DhQixatEijPS4uDnZ2dvoKn4iIiAwoPz8fY8aMQXZ2ttrpNPpmUoWRNoMGDYJEIsH27du1Lte2x8jLyws3b97U+werUCiQkJCA0NBQWFlZ6XXsusDc8wPMP0fmZ/rMPUfmZ/oMlWNOTg5cXFwMXhiZ5KG0B3Xp0gXr168vd7lMJoNMJtNot7KyMtiX0pBj1wXmnh9g/jkyP9Nn7jkyP9On7xxr6/My+fsYpaSkwN3d3dhhEBERkRkw6h6jvLw8/Pvvv6r3ly5dwsmTJ9GwYUM0adIE8+bNQ3p6OtatWwcAiIqKgo+PD9q1a4fCwkKsX78e8fHxiI+PN1YKREREZEaMWhgdO3ZM7Yqy2bNnAwAiIiIQGxuLjIwMpKWlqZYXFhZizpw5SE9Ph62tLdq1a4cdO3agf//+tR47ERERmR+jFka9evVCRed+x8bGqr2fO3cu5s6da+CoiIiI6FFl8ucYEREREekLCyMiIiIiJRZGREREREosjIiIiIiUWBgRERERKbEwIiIiIlJiYURERESkxMKIiIiISImFEREREZESCyMiIiIiJRZGREREREosjIiIiIiU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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.title(\"User Movie Preference\", size=15) \n",
    "plt.xlabel(\"Movie A rating\")\n",
    "plt.ylabel(\"Movie B rating\")\n",
    "plt.grid()\n",
    "\n",
    "# 绘制原始样本点，要求不同的影片喜好类别用不同的颜色标记\n",
    "# 提示: 用scatter散点图绘制，用它的参数c实现不同的类别用不同的颜色标记\n",
    "plt.scatter(X[:, 0], X[:, 1], c=y, cmap='viridis', s=100, alpha=0.7, edgecolors='black')\n",
    "\n",
    "# 绘制新数据点，用红色x标记，大小为8\n",
    "# 提示：用plt.plot()绘制，用它的参数marker实现不同的标记符号\n",
    "plt.plot(new_user[0, 0], new_user[0, 1], 'rx', markersize=8, markeredgewidth=2)\n",
    "\n",
    "# 新数据最近邻索引为第一个最近邻的索引\n",
    "dist, idx = knn.kneighbors(new_user, n_neighbors=1)\n",
    "nearest = X[idx[0][0]]  # 获取最近邻点的坐标，这是一个列表，第一个元素是x坐标，第二个元素是y坐标\n",
    "\n",
    "# 用红线标记新数据点与最近邻点的连接线\n",
    "# 提示：用plt.plot()绘制，用 r-- 实现红色虚线\n",
    "plt.plot([new_user[0, 0], nearest[0]], [new_user[0, 1], nearest[1]], 'r--', linewidth=2)\n",
    "\n",
    "# 为每个点添加坐标文本  \n",
    "for x, y in zip(X[:, 0], X[:, 1]):\n",
    "    plt.text(x, y+0.1, f'({x}, {y})') \n",
    "\n",
    "# 为新数据点添加坐标文本\n",
    "plt.text(new_user[0,0], new_user[0,1]+0.1, f'({new_user[0,0]}, {new_user[0,1]})')\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "movie_env",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.18"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
